# 接下来我们将通过PyQt5记事本的案例实现来学习 PyQt5当中消息和槽机制的学习
#    另外通过该实例，我们将学习PyQt5当中Dialog的基本了解

# 首先依旧是导入相关的模块
from PyQt5.QtWidgets import *
# 由于我们要实现的是记事本，那还需要一些快捷键，所以还要导入下面的模块
from PyQt5.QtGui import QKeySequence

# 主界面 (也就是定义一个继承了QMainWindow的主窗体)


class MainWindow(QMainWindow):
    # 在记事本弹窗类当中，我们需要设置一些格外功能，即当叉掉弹窗的时候，我们还需要弹出一个‘是否保存’的选择框
    # 选择过后，才是真正的退出
    # 那么我们就需要重写一下父类（QMainWindow）当中的close方法
    def closeEvent(self, e) -> None:
        if not text.document().isModified():
            # 如果文档没有被修改，那么想要关闭的时候就直接关闭
            return
        # 如果记事本内容被修改了，那么关闭记事本窗口时候，需要弹出是否保存这样的提示框
        answer = QMessageBox.question(window, '关闭之前查看', '关闭之前是否保存文件',
                                      QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
        # 接下来还需要对你选择的答案进行处理
        if answer & QMessageBox.Save:
            save()
        elif answer & QMessageBox.Cancel:
            e.ignore()


# 创建一个app对象
app = QApplication([])
# 为app对象起名字
app.setApplicationName('文本编辑器')
# 创建一个文本编辑对象（也就是用来输入文本的组件）
text = QPlainTextEdit()
# 创建一个窗体对象
window = MainWindow()
# 设置窗体对象的初始化大小
window.setMinimumSize(800, 600)
# 把文本对象放入窗体当中
window.setCentralWidget(text)


# 菜单项的添加 #

# ’文件菜单‘的添加
menu = window.menuBar().addMenu('文件')
# 在文件菜单下通常会有’打开‘’保存‘等指令
open_action = QAction('打开')                  # 创建’打开‘操作对象
open_action.setShortcut(QKeySequence.Open)    # 为’打开‘指令添加快捷键
# 注意：这里我们添加了’打开‘指令，但是如果不进行信号和槽的处理，那么运行时点击并不会有任何反应。因此这里需要进行信号和槽的处理

# 创建一个文件路径的变量（全局变量）
file_path = None
# '打开'操作触发的函数


def open_file():

    global file_path    # 声明全局变量 这样就可以在此函数中调用和修改该值
    path = QFileDialog.getOpenFileName(window, 'open')[0]

    # 'GetOpenFilename相当于Excel打开窗口，通过该窗口选择要打开的文件，并可以返回选择的文件完整路径和文件名。
    # 注意path 返回的是完整路径和文件名

    # 按照教程是使用来达到再窗体中显示文件内容，但是会出现莫名报错 ：进程已结束,退出代码-1073740791 (0xC0000409)
    if path:
    #    如果path被选中，那么就显示出text所有的内容
    #    text.setPalette(open(path).read())    # 找到问题所在：这里不能使用setPalette来实现将文件内容显示在窗体的功能
        text.setPlainText(open(path, encoding='utf-8').read())
        file_path = path

    # 因此我们可以自己整理一下思路：
    # 我们现在是想要实现打开指令，而打开指令的基本逻辑是：
    # 获取打开文件的路径--》读取文件的内容--》将读取的内容显示在我们设计的窗体当中 --》紧跟保存等后续程序
    # context = open(path, encoding='utf-8').read()
    # text.setPlainText(context)
    # text.setPlainText(open(path, encoding='utf-8').read())
    # 上面的path就是需要打开的文件路径，open().read()就是读取文件内容，而setPlainText就是实现将文件内容显示在我们设计好记事本界面当中
    # 注意：之前获取的路径需要保存，以便于后续的保存指令使用
    # file_path = path


open_action.triggered.connect(open_file)      # ’打开‘操作触发时所连接的触发事件（函数）


# ’保存‘指令的添加
# ’保存‘指令触发的事件
def save():
    if file_path is None:
        # 通过文件路径（file_path）是否存在，来进行保存选择
        pass
    else:
        with open(file_path, 'w', encoding='utf-8') as f:
            f.write(text.toPlainText())
        #  注意的一行指令是设置文本内容不能进行修改，这条指令添加后容易造成程序运行错误
        # text.document().setModified(False)     # 让文本内的内容不能被修改（文本内的内容不能再编辑了）


save_action = QAction('保存')   # 创建一个保存的启动对象
save_action.setShortcut(QKeySequence.Save)   # 设置’保存‘指令的快捷方式
save_action.triggered.connect(save)          # 保存指令被点击时候所触发的事件

# ‘另存为’指令的添加
save_as_action = QAction('另存为')
# 创建‘另存为’指令触发时候的响应事件（当save_as_action有信号时，要给设置一个相应的相应槽）
def save_as():
    global file_path  # 将另存为的默认路径就设置为打开文件所在位置
    path = QFileDialog.getSaveFileName(window, '另存为')[0]
    if path:
        file_path = path
        save()
# 接下来就是设置‘另存为’指令的连接槽
save_as_action.triggered.connect(save_as)
# 注意这里使用connect连接槽时候内部是函数名，不带括号

# ‘退出’
exit_action = QAction('退出')      # 先创建一个（退出）动作对象
# ‘退出’指令简单来说就是完成窗口的关闭
exit_action.setShortcut(QKeySequence.Close)
# 退出指令信号（exit_action）被触发时（.triggered）对应连接（connect）的槽是（window.close）
exit_action.triggered.connect(window.close)


menu.addAction(open_action)                   # 将’打开‘操作对象添加到文件菜单中
menu.addAction(save_action)                   # 将’保存‘操作添加到文件菜单中
menu.addAction(save_as_action)                # 将‘另存为’操作添加到文件菜单中
# 在实际的文件菜单中我们一般会看到分割线
menu.addSeparator()           # 文件菜单中的‘退出’键上添加一个分割线
menu.addAction(exit_action)                   # 将‘退出’操作添加到文件菜单中

# 接下来我们添加一个‘帮助’菜单
help_menu = window.menuBar().addMenu('帮助')
about_action = QAction('关于')
# 而关于当中需要有相应的提示内容 接下来我们就定义一下这个关于的内容函数
def show_about_dialog():
    about_text = "<center>这是一个简易的文本编译器</center><p>版本1.0</p>"   # 其中的文本我们可以使用html标签来写
    QMessageBox.about(window, '说明', about_text)     # 也就是将帮助菜单当中的‘关于’指令的事件触发以弹窗的形式呈现

# 将‘关于’指令的触发信号和槽连接起来
about_action.triggered.connect(show_about_dialog)
# 然后将‘关于’指令添加进帮助菜单中去
help_menu.addAction(about_action)



# 执行（测试）
window.show()    # 显示窗体
app.exec_()      # app程序的循环监测


# 到此为止，这样一个关于记事本的小程序就算完成了，最主要的思想就是：组件触发时的信号和一个槽（函数）的关联
#      理论上信号与槽之间的连接时对象（组件对象 和 函数对象）之间的数据传递

# 另外还有一个重点：对于父类当中的方法重写